# Copyright (c) 2014, 2025, Oracle and/or its affiliates.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is designed to work with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms,
# as designated in a particular file or component or in included license
# documentation.  The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have either included with
# the program or referenced in the documentation.
#
# This program is distributed in the hope that it will be useful,  but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
# the GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
MESSAGE(STATUS "Running cmake version ${CMAKE_VERSION}")

IF(MYSQL_SOURCE_DIR)
  FILE(TO_CMAKE_PATH "${MYSQL_SOURCE_DIR}" MYSQL_SOURCE_DIR)
ENDIF()

IF(MYSQL_BUILD_DIR)
  FILE(TO_CMAKE_PATH "${MYSQL_BUILD_DIR}" MYSQL_BUILD_DIR)
ENDIF()

IF(WIN32)
  # CMake 3.8.0 is needed for Visual Studio 2017 and x64 toolset.
  CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)
  EXECUTE_PROCESS(COMMAND wmic os get version
    OUTPUT_VARIABLE NT_RELEASE_VERSION
    OUTPUT_STRIP_TRAILING_WHITESPACE
    RESULT_VARIABLE WMIC_RESULT
    )
  IF(WMIC_RESULT EQUAL 0)
    STRING(REPLACE "\r" "" NT_RELEASE_VERSION "${NT_RELEASE_VERSION}")
    STRING(REPLACE "\n" "" NT_RELEASE_VERSION "${NT_RELEASE_VERSION}")
    STRING(REGEX MATCH "Version[ ]+([0-9\.]+)" V_NUM "${NT_RELEASE_VERSION}")
    MESSAGE(STATUS "NT_RELEASE_VERSION is ${NT_RELEASE_VERSION}")
    IF(CMAKE_MATCH_1)
      IF(CMAKE_MATCH_1 VERSION_LESS "10")
        MESSAGE(FATAL_ERROR
          "Need at least Windows Server 2016, or Windows 10, to build")
      ENDIF()
    ENDIF()
  ENDIF()
  IF(CMAKE_GENERATOR MATCHES "Visual Studio" AND CMAKE_GENERATOR MATCHES "2019")
    IF(CMAKE_VERSION MATCHES "MSVC")
      # It is the bundled version, ignore version check,
      # (although this seems to be buggy too).
    ELSE()
      # Bug in msbuild, install the latest in the 3.15 series as a workaround.
      # https://gitlab.kitware.com/cmake/cmake/issues/19303
      # custom commands are re-built every time
      CMAKE_MINIMUM_REQUIRED(VERSION 3.15.3)
    ENDIF()
  ENDIF()
ELSEIF(APPLE)
  # Version 3.12.4 is needed because the new build system of Xcode is not
  # supported by cmake. 3.12.4 will force using the legacy build system.
  # Version 3.9.2 is needed because INCLUDE_DIRECTORIES(SYSTEM ...) wasn't
  # handled properly by the cmake Xcode generator.
  IF(CMAKE_GENERATOR STREQUAL "Xcode")
    CMAKE_MINIMUM_REQUIRED(VERSION 3.12.4)
  ELSE()
    CMAKE_MINIMUM_REQUIRED(VERSION 3.9.2)
  ENDIF()
  # If this is macOS 11, we need cmake 3.18
  # System libraries like
  #    /usr/lib/libresolv.dylib
  # are no longer present in the file system.
  # cmake >= 3.18 will look for .tbd files in the SDK instead
  # So we end up linking with:
  #    /Applications/Xcode.app/.../usr/lib/libresolv.tbd
  # We must postpone the version test until we have called 'uname -r' below.
ELSEIF(UNIX)
  # This is currently minimum version on all supported platforms.
  IF(CMAKE_VERSION VERSION_LESS 3.5.1)
    # Default cmake is 2.8.12.2 on RedHat
    IF(EXISTS "/etc/redhat-release")
      MESSAGE(WARNING "Please use cmake3 rather than cmake on this platform")
      FIND_PROGRAM(MY_CMAKE3 cmake3 /bin /usr/bin /usr/local/bin)
      IF(MY_CMAKE3)
        MESSAGE(STATUS "Found ${MY_CMAKE3}")
      ELSE()
        MESSAGE(STATUS "Please install cmake3 (yum install cmake3)")
      ENDIF()
    ELSE()
      # On SunOS /opt/csw/bin/cmake is (most likely) too old.
      FIND_PROGRAM(MY_UNAME uname /bin /usr/bin /usr/local/bin /sbin)
      IF(MY_UNAME)
        EXEC_PROGRAM(uname ARGS -s OUTPUT_VARIABLE MY_HOST_SYSTEM_NAME)
        IF(MY_HOST_SYSTEM_NAME MATCHES "SunOS")
          FIND_PROGRAM(MY_CMAKE cmake /usr/bin
            NO_CMAKE_ENVIRONMENT_PATH
            NO_SYSTEM_ENVIRONMENT_PATH
            )
          IF(MY_CMAKE)
            MESSAGE(STATUS "Found ${MY_CMAKE}")
            EXEC_PROGRAM(${MY_CMAKE} ARGS --version)
          ELSE()
            MESSAGE(STATUS "Please install /usr/bin/cmake ")
          ENDIF()
        ENDIF()
      ENDIF()
    ENDIF()
  ENDIF()
ENDIF()

# CMake 3.5 is needed for TARGET_SOURCES(... $<TARGET_OBJECTS:${LIB}_objlib>)
# CMake 3.7 is needeed for VERSION_GREATER_EQUAL
# For the set of currently supported platforms, we can bump up to:
CMAKE_MINIMUM_REQUIRED(VERSION 3.14.6)
INCLUDE(CMakePushCheckState)

# On Linux el6/el7/el8/el9 the default gcc is too old,
# see if devtoolset/gcc-toolset is installed.
# Same with SUSE linux, look for gcc 10/12 there.
# We need to look for gcc before calling PROJECT below.
OPTION(FORCE_UNSUPPORTED_COMPILER "Disable compiler version checks" OFF)
MARK_AS_ADVANCED(FORCE_UNSUPPORTED_COMPILER)

# Use 'uname -r' and 'rpm -qf /' to figure out host system.
# For Docker images we cannot trust uname, so use rpm instead.
IF(UNIX)
  FIND_PROGRAM(MY_UNAME uname /bin /usr/bin /usr/local/bin /sbin)
  IF(MY_UNAME)
    EXECUTE_PROCESS(COMMAND ${MY_UNAME} -s
      OUTPUT_VARIABLE MY_HOST_SYSTEM_NAME
      OUTPUT_STRIP_TRAILING_WHITESPACE
      RESULT_VARIABLE MY_UNAME_RESULT
      )
    EXECUTE_PROCESS(COMMAND ${MY_UNAME} -m
      OUTPUT_VARIABLE MY_HOST_MACHINE_NAME
      OUTPUT_STRIP_TRAILING_WHITESPACE
      RESULT_VARIABLE MY_UNAME_MACHINE_RESULT
      )
    EXEC_PROGRAM(${MY_UNAME} ARGS -r OUTPUT_VARIABLE MY_HOST_SYSTEM_VERSION)
  ENDIF()
  FIND_PROGRAM(MY_DPKG_BUILDFLAGS dpkg-buildflags /bin /usr/bin)
  FIND_PROGRAM(MY_RPM rpm /bin /usr/bin)
  IF(MY_RPM)
    EXECUTE_PROCESS(COMMAND ${MY_RPM} -qf /
      OUTPUT_VARIABLE MY_HOST_FILESYSTEM_NAME
      OUTPUT_STRIP_TRAILING_WHITESPACE
      RESULT_VARIABLE MY_RPM_RESULT
      )
  ENDIF()
ENDIF()

# STRING(APPEND ...) from cmake VERSION 3.4
MACRO(STRING_APPEND STRING_VAR INPUT)
  SET(${STRING_VAR} "${${STRING_VAR}}${INPUT}")
ENDMACRO()

MACRO(STRING_PREPEND STRING_VAR INPUT)
  SET(${STRING_VAR} "${INPUT}${${STRING_VAR}}")
ENDMACRO()

IF(MY_HOST_SYSTEM_NAME MATCHES "SunOS")
  SET(SOLARIS 1)
ENDIF()

IF(MY_HOST_SYSTEM_NAME MATCHES "Linux")
  # Trust 'rpm -qf /' rather than 'uname -s'
  STRING(REGEX MATCH "\\.el([6789])\\." MATCH_FSYS "${MY_HOST_FILESYSTEM_NAME}")
  IF(NOT CMAKE_MATCH_1)
    STRING(REGEX MATCH "\\.el(10)\\." MATCH_FSYS "${MY_HOST_FILESYSTEM_NAME}")
  ENDIF()

  # Set LINUX_RHEL6, LINUX_RHEL7, LINUX_RHEL8 or LINUX_RHEL9 or LINUX_RHEL10
  IF(CMAKE_MATCH_1)
    SET(LINUX_RHEL 1)
    SET(LINUX_RHEL${CMAKE_MATCH_1} 1)
  ENDIF()
ENDIF()

IF(NOT LINUX_RHEL AND MY_HOST_SYSTEM_NAME MATCHES "Linux")
  IF(EXISTS "/etc/os-release")
    FILE(READ "/etc/os-release" MY_OS_RELEASE)
    IF(MY_OS_RELEASE MATCHES "Debian")
      SET(LINUX_DEBIAN 1)
    ELSEIF(MY_OS_RELEASE MATCHES "Ubuntu")
      SET(LINUX_UBUNTU 1)
      IF(MY_OS_RELEASE MATCHES "16.04")
        SET(LINUX_UBUNTU_16_04 1)
      ELSEIF(MY_OS_RELEASE MATCHES "18.04")
        SET(LINUX_UBUNTU_18_04 1)
      ENDIF()
      # /etc/os-release contains a line like
      # VERSION_ID="20.04"
      # Match the numeric value, including the dot, ignore the rest:
      STRING(REGEX MATCH
        "VERSION_ID=\"([0-9\\.]+).*" UNUSED ${MY_OS_RELEASE})
      IF(CMAKE_MATCH_1)
        SET(LINUX_UBUNTU_VERSION_ID ${CMAKE_MATCH_1})
      ENDIF()
    ENDIF()
  ENDIF()
ENDIF()

IF(EXISTS "/etc/SuSE-release")
  SET(LINUX_SUSE 1)
ENDIF()

IF(LINUX_SUSE)
  FILE(READ "/etc/SuSE-release" MY_OS_RELEASE)
  IF(MY_OS_RELEASE MATCHES "SUSE Linux Enterprise Server 12")
    SET(LINUX_SUSE_12 1)
  ELSEIF(MY_OS_RELEASE MATCHES "SUSE Linux Enterprise Server 15" OR
      MY_OS_RELEASE MATCHES "openSUSE .* 15")
    SET(LINUX_SUSE_15 1)
  ELSE()
    MESSAGE(WARNING "Unknown SUSE version.")
  ENDIF()
ENDIF()

IF(CMAKE_HOST_UNIX AND NOT FORCE_UNSUPPORTED_COMPILER
    AND NOT CMAKE_C_COMPILER AND NOT CMAKE_CXX_COMPILER)
  # Cannot INCLUDE(CMakeDetermineSystem) prior to PROJECT initialization below.
  SET (ENV_CC "$ENV{CC}")
  SET (ENV_CXX "$ENV{CXX}")
  IF (ENV_CC STREQUAL "" AND ENV_CXX STREQUAL "")
    IF(LINUX_RHEL)
      MESSAGE(STATUS "This is ${MATCH_FSYS} as found from 'rpm -qf /'")
    ENDIF()
    IF(LINUX_RHEL AND NOT LINUX_RHEL10)
      MESSAGE(STATUS "Looking for a devtoolset compiler")
      IF(LINUX_RHEL6)
        SET(ALTERNATIVE_PATHS "/opt/rh/devtoolset-8")
      ELSEIF(LINUX_RHEL7)
        # gcc11 not available yet
        IF(MY_HOST_MACHINE_NAME MATCHES "aarch64")
          SET(ALTERNATIVE_PATHS "/opt/rh/devtoolset-10")
        ELSE()
          SET(ALTERNATIVE_PATHS "/opt/rh/devtoolset-11")
        ENDIF()
      ELSEIF(LINUX_RHEL8 OR LINUX_RHEL9)
        SET(ALTERNATIVE_PATHS "/opt/rh/gcc-toolset-13")
      ELSEIF(LINUX_RHEL10)
        # Use default /usr/bin/gcc which is 14.2.1
      ENDIF()

      FOREACH(OPT_PATH ${ALTERNATIVE_PATHS})
        FIND_PROGRAM(ALTERNATIVE_GCC gcc
          NO_DEFAULT_PATH
          PATHS "${OPT_PATH}/root/usr/bin")
        FIND_PROGRAM(ALTERNATIVE_GPP g++
          NO_DEFAULT_PATH
          PATHS "${OPT_PATH}/root/usr/bin")
        FIND_PROGRAM(ALTERNATIVE_LD ld
          NO_DEFAULT_PATH
          PATHS "${OPT_PATH}/root/usr/bin")
        FIND_PROGRAM(ALTERNATIVE_AR ar
          NO_DEFAULT_PATH
          PATHS "${OPT_PATH}/root/usr/bin")
        FIND_PROGRAM(ALTERNATIVE_RANLIB ranlib
          NO_DEFAULT_PATH
          PATHS "${OPT_PATH}/root/usr/bin")
        FIND_PROGRAM(ALTERNATIVE_ENABLE enable
          NO_DEFAULT_PATH
          PATHS "${OPT_PATH}")
      ENDFOREACH()
      # A missing ALTERNATIVE_LD may generate bad executables.
      IF(ALTERNATIVE_GCC AND ALTERNATIVE_GPP AND ALTERNATIVE_LD)
        # Set correct search path for executables, libraries, and data files.
        GET_FILENAME_COMPONENT(GCC_B_PREFIX ${ALTERNATIVE_GCC} DIRECTORY)
        STRING_PREPEND(CMAKE_C_FLAGS "-B${GCC_B_PREFIX} ")
        GET_FILENAME_COMPONENT(GPP_B_PREFIX ${ALTERNATIVE_GPP} DIRECTORY)
        STRING_PREPEND(CMAKE_CXX_FLAGS "-B${GPP_B_PREFIX} ")

        SET(CMAKE_C_COMPILER ${ALTERNATIVE_GCC})
        SET(CMAKE_CXX_COMPILER ${ALTERNATIVE_GPP})
        SET(CMAKE_LINKER ${ALTERNATIVE_LD})
        SET(CMAKE_LINKER ${ALTERNATIVE_LD} CACHE PATH "Alternative ld")
        IF(ALTERNATIVE_AR)
          SET(CMAKE_AR ${ALTERNATIVE_AR})
          SET(CMAKE_AR ${ALTERNATIVE_AR} CACHE PATH "Alternative ar")
        ENDIF()
        IF(ALTERNATIVE_RANLIB)
          SET(CMAKE_RANLIB ${ALTERNATIVE_RANLIB})
          SET(CMAKE_RANLIB ${ALTERNATIVE_RANLIB} CACHE PATH "Alternative ranlib")
        ENDIF()
        MESSAGE(STATUS "Using ${ALTERNATIVE_GCC}")
        MESSAGE(STATUS "Using ${ALTERNATIVE_GPP}")
      ELSE()
        IF(LINUX_RHEL6)
          SET(DEV_PACKAGES
            "devtoolset-8-gcc devtoolset-8-gcc-c++ devtoolset-8-binutils")
        ELSEIF(LINUX_RHEL7)
          IF(MY_HOST_MACHINE_NAME MATCHES "aarch64")
            SET(DEV_PACKAGES
              "devtoolset-10-gcc devtoolset-10-gcc-c++ devtoolset-10-binutils")
          ELSE()
            SET(DEV_PACKAGES
              "devtoolset-11-gcc devtoolset-11-gcc-c++ devtoolset-11-binutils")
          ENDIF()
        ELSEIF(LINUX_RHEL8 OR LINUX_RHEL9)
          SET(DEV_PACKAGES
            "gcc-toolset-13-gcc gcc-toolset-13-gcc-c++ gcc-toolset-13-binutils")
          STRING_APPEND(DEV_PACKAGES " gcc-toolset-13-annobin-annocheck")
          STRING_APPEND(DEV_PACKAGES " gcc-toolset-13-annobin-plugin-gcc")
        ENDIF()
        MESSAGE(WARNING
          "Could not find devtoolset compiler/linker in ${ALTERNATIVE_PATHS}")
        MESSAGE(WARNING "You need to install the required packages:\n"
          " yum install ${DEV_PACKAGES}\n")
        MESSAGE(FATAL_ERROR
          "Or you can set CMAKE_C_COMPILER and CMAKE_CXX_COMPILER explicitly.")
      ENDIF()

    ELSEIF(LINUX_UBUNTU_18_04)
      MESSAGE(STATUS "Looking for gcc-8 on Ubuntu 18.04")
      FIND_PROGRAM(ALTERNATIVE_GCC gcc-8
        NO_DEFAULT_PATH
        PATHS "/usr/bin")
      FIND_PROGRAM(ALTERNATIVE_GPP g++-8
        NO_DEFAULT_PATH
        PATHS "/usr/bin")
      FIND_PROGRAM(GCC_AR_EXECUTABLE gcc-ar-8
        NO_DEFAULT_PATH
        PATHS "/usr/bin")
      FIND_PROGRAM(GCC_RANLIB_EXECUTABLE gcc-ranlib-8
        NO_DEFAULT_PATH
        PATHS "/usr/bin")
      IF(GCC_AR_EXECUTABLE)
        SET(CMAKE_AR ${GCC_AR_EXECUTABLE})
        SET(CMAKE_AR ${GCC_AR_EXECUTABLE} CACHE PATH "Alternative ar")
      ENDIF()
      IF(GCC_RANLIB_EXECUTABLE)
        SET(CMAKE_RANLIB ${GCC_RANLIB_EXECUTABLE})
        SET(CMAKE_RANLIB ${GCC_RANLIB_EXECUTABLE} CACHE PATH "Alternative ranlib")
      ENDIF()
      IF (ALTERNATIVE_GCC AND ALTERNATIVE_GPP)
        SET(CMAKE_C_COMPILER ${ALTERNATIVE_GCC})
        SET(CMAKE_CXX_COMPILER ${ALTERNATIVE_GPP})
        MESSAGE(STATUS "Using ${ALTERNATIVE_GCC}")
        MESSAGE(STATUS "Using ${ALTERNATIVE_GPP}")
      ELSE()
        MESSAGE(WARNING "Could not find gcc-8")
        MESSAGE(FATAL_ERROR
          "Please do 'apt install gcc-8 g++-8'\n"
          "or set CMAKE_C_COMPILER and CMAKE_CXX_COMPILER explicitly.")
      ENDIF()

    ELSEIF(LINUX_SUSE_12 OR LINUX_SUSE_15)
      MESSAGE(STATUS "We need to look for a newer GCC on SUSE Linux.")
      IF(LINUX_SUSE_12)
        FIND_PROGRAM(ALTERNATIVE_GCC gcc-10
          NO_DEFAULT_PATH
          PATHS "/usr/bin")
        FIND_PROGRAM(ALTERNATIVE_GPP g++-10
          NO_DEFAULT_PATH
          PATHS "/usr/bin")
        FIND_PROGRAM(GCC_AR_EXECUTABLE gcc-ar-10
          NO_DEFAULT_PATH
          PATHS "/usr/bin")
        FIND_PROGRAM(GCC_RANLIB_EXECUTABLE gcc-ranlib-10
          NO_DEFAULT_PATH
          PATHS "/usr/bin")
      ELSEIF(LINUX_SUSE_15)
        FIND_PROGRAM(ALTERNATIVE_GCC gcc-12
          NO_DEFAULT_PATH
          PATHS "/usr/bin")
        FIND_PROGRAM(ALTERNATIVE_GPP g++-12
          NO_DEFAULT_PATH
          PATHS "/usr/bin")
        FIND_PROGRAM(GCC_AR_EXECUTABLE gcc-ar-12
          NO_DEFAULT_PATH
          PATHS "/usr/bin")
        FIND_PROGRAM(GCC_RANLIB_EXECUTABLE gcc-ranlib-12
          NO_DEFAULT_PATH
          PATHS "/usr/bin")
      ENDIF()
      IF(GCC_AR_EXECUTABLE)
        SET(CMAKE_AR ${GCC_AR_EXECUTABLE})
        SET(CMAKE_AR ${GCC_AR_EXECUTABLE} CACHE PATH "Alternative ar")
      ENDIF()
      IF(GCC_RANLIB_EXECUTABLE)
        SET(CMAKE_RANLIB ${GCC_RANLIB_EXECUTABLE})
        SET(CMAKE_RANLIB ${GCC_RANLIB_EXECUTABLE} CACHE PATH "Alternative ranlib")
      ENDIF()
      IF (ALTERNATIVE_GCC AND ALTERNATIVE_GPP)
        SET(CMAKE_C_COMPILER ${ALTERNATIVE_GCC})
        SET(CMAKE_CXX_COMPILER ${ALTERNATIVE_GPP})
        MESSAGE(STATUS "Using ${ALTERNATIVE_GCC}")
        MESSAGE(STATUS "Using ${ALTERNATIVE_GPP}")
        # Use the new ABI so that std::string can be used with allocators
        # that are not default-constructible (e.g. Memroot_allocator)
        IF(LINUX_SUSE_12)
          ADD_DEFINITIONS(-D_GLIBCXX_USE_CXX11_ABI=1)
        ENDIF()
      ELSE()
        MESSAGE(WARNING "Could not find newer gcc.")
        IF(LINUX_SUSE_12)
          MESSAGE(FATAL_ERROR "Please do zypper install gcc10 gcc10-c++\n"
            "or set CMAKE_C_COMPILER and CMAKE_CXX_COMPILER explicitly.")
        ELSE()
          MESSAGE(FATAL_ERROR "Please do zypper install gcc12 gcc12-c++\n"
            "or set CMAKE_C_COMPILER and CMAKE_CXX_COMPILER explicitly.")
        ENDIF()
      ENDIF()
    ELSEIF(SOLARIS)
      MESSAGE(STATUS "Looking for GCC 10 on Solaris.")
      FIND_PROGRAM(ALTERNATIVE_GCC gcc
        NO_DEFAULT_PATH
        PATHS "/usr/gcc/10/bin")
      FIND_PROGRAM(ALTERNATIVE_GPP g++
        NO_DEFAULT_PATH
        PATHS "/usr/gcc/10/bin")
      IF (ALTERNATIVE_GCC AND ALTERNATIVE_GPP)
        SET(CMAKE_C_COMPILER ${ALTERNATIVE_GCC})
        SET(CMAKE_CXX_COMPILER ${ALTERNATIVE_GPP})
        MESSAGE(STATUS "Using ${ALTERNATIVE_GCC}")
        MESSAGE(STATUS "Using ${ALTERNATIVE_GPP}")
      ELSE()
        MESSAGE(WARNING "Could not find /usr/gcc/10/bin/gcc")
      ENDIF()
    ENDIF()
  ENDIF()
ENDIF()

project(mysqlsh)

IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  SET(MY_COMPILER_IS_CLANG 1)
ELSEIF(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
  SET(MY_COMPILER_IS_GNU 1)
  IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9 AND
      NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)
    SET(MY_COMPILER_IS_GNU_8 1)
  ENDIF()
ELSEIF(CMAKE_CXX_COMPILER_ID MATCHES "SunPro")
  SET(MY_COMPILER_IS_SUNPRO 1)
ENDIF()

IF(MY_COMPILER_IS_CLANG OR MY_COMPILER_IS_GNU)
  SET(MY_COMPILER_IS_GNU_OR_CLANG 1)
ENDIF()

# Include the platform-specific file. To allow exceptions, this code
# looks for files in order of how specific they are. If there is, for
# example, a generic Linux.cmake and a version-specific
# Linux-2.6.28-11-generic, it will pick Linux-2.6.28-11-generic and
# include it. It is then up to the file writer to include the generic
# version if necessary.
FOREACH(_base
    ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_VERSION}-${CMAKE_SYSTEM_PROCESSOR}
    ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_VERSION}
    ${CMAKE_SYSTEM_NAME})
  SET(_file ${CMAKE_SOURCE_DIR}/cmake/os/${_base}.cmake)
  IF(EXISTS ${_file})
    INCLUDE(${_file})
    BREAK()
  ENDIF()
ENDFOREACH()

IF(CMAKE_VERSION VERSION_GREATER "3.12.0")
  CMAKE_POLICY(SET CMP0075 NEW)
ENDIF()

set(CMAKE_MODULE_PATH
    ${CMAKE_SOURCE_DIR}/cmake
    ${CMAKE_MODULE_PATH}
)

IF(LINUX_RHEL10 AND MY_HOST_MACHINE_NAME MATCHES "aarch64")
  SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
  MESSAGE(STATUS "Building with PIC")
ENDIF()

include(TestBigEndian)
TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
add_definitions(-DIS_BIG_ENDIAN=${IS_BIG_ENDIAN})

### Configuring package year
IF(NOT PACKAGE_YEAR)
  SET(PACKAGE_YEAR "2025")
ENDIF()
add_definitions(-DPACKAGE_YEAR="${PACKAGE_YEAR}")

IF(WITH_TESTS)
  ENABLE_TESTING()
ENDIF()

###
### Initial configuration
###
INCLUDE(version.cmake)

###
### Detect Stuff
###
INCLUDE(link_options)
INCLUDE(cpu_info)
INCLUDE(install_macros)
INCLUDE(exeutils)
INCLUDE(libutils)
INCLUDE(compiler)
INCLUDE(static_analysis)
include(compile_flags)
INCLUDE(msvc)

# Set correct search path for executables, libraries, and data files.
IF(LINUX_RHEL)
  IF(ALTERNATIVE_GCC)
    GET_FILENAME_COMPONENT(GCC_B_PREFIX ${ALTERNATIVE_GCC} DIRECTORY)
    STRING_PREPEND(CMAKE_C_FLAGS "-B${GCC_B_PREFIX} ")
  ENDIF()
  IF(ALTERNATIVE_GPP)
    GET_FILENAME_COMPONENT(GPP_B_PREFIX ${ALTERNATIVE_GPP} DIRECTORY)
    STRING_PREPEND(CMAKE_CXX_FLAGS "-B${GPP_B_PREFIX} ")
  ENDIF()
ENDIF()

IF(CMAKE_C_FLAGS MATCHES " -flto" OR CMAKE_CXX_FLAGS MATCHES " -flto")
  SET(CMAKE_COMPILER_FLAG_WITH_LTO 1)
ENDIF()

include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles)

FUNCTION(MY_CHECK_CXX_COMPILER_WARNING WARNING_OPTION RETURN_VALUE)
  # Strip off possible leading -Wno- or -W
  STRING(REGEX REPLACE "^-Wno-" "" WARNING_OPTION ${WARNING_OPTION})
  STRING(REGEX REPLACE "^-W" "" WARNING_OPTION ${WARNING_OPTION})
  STRING(REGEX MATCH "^error=" ERROR_PREFIX ${WARNING_OPTION})
  # Use HAVE_CXX_W_warning_option as cache variable.
  STRING(REPLACE "-" "_" VAR_NAME ${WARNING_OPTION})
  STRING(REPLACE "=" "_" VAR_NAME ${VAR_NAME})
  STRING(REPLACE "c++" "cpp" VAR_NAME ${VAR_NAME})
  SET(VAR_NAME "HAVE_CXX_W_${VAR_NAME}")
  IF(MY_COMPILER_IS_GNU_OR_CLANG)
    IF(SHOW_SUPPRESSED_COMPILER_WARNINGS AND NOT ERROR_PREFIX)
      MY_CHECK_CXX_COMPILER_FLAG("-Werror=${WARNING_OPTION}" ${VAR_NAME})
      IF(${VAR_NAME})
        SET(WARNING_OPTION "error=${WARNING_OPTION}")
      ENDIF()
    ENDIF()
    IF(NOT ${VAR_NAME})
      MY_CHECK_CXX_COMPILER_FLAG("-W${WARNING_OPTION}" ${VAR_NAME})
    ENDIF()
  ELSE()
    SET(${VAR_NAME} 0 CACHE INTERNAL "-W${WARNING_OPTION} not supported")
  ENDIF()
  IF(${VAR_NAME})
    SET(${RETURN_VALUE} "-Wno-${WARNING_OPTION}" PARENT_SCOPE)
  ELSE()
    SET(${RETURN_VALUE} 0 PARENT_SCOPE)
  ENDIF()
ENDFUNCTION()

FUNCTION(GET_MAJOR_MINOR input_ver output_var)
  STRING(REGEX REPLACE "^([0-9]+)\\.([0-9]+).*" "\\1.\\2" input_ver "${input_ver}")
  SET(${output_var} ${input_ver} PARENT_SCOPE)
ENDFUNCTION()

# Use lld for Clang if available and not explicitly disabled.
# Also works for gcc on Debian/Ubuntu. Do 'apt install lld'.
# LTO build fails with lld, so turn it off by default.
IF(LINUX AND NOT WITH_LTO AND NOT CMAKE_COMPILER_FLAG_WITH_LTO)
  OPTION(USE_LD_LLD "Use llvm lld linker" ON)
ELSE()
  OPTION(USE_LD_LLD "Use llvm lld linker" OFF)
ENDIF()

IF(USE_LD_LLD)
  CMAKE_PUSH_CHECK_STATE(RESET)

  # RPM builds on Fedora use -specs files.
  # This may cause the test for -fuse-ld=lld to succeed, when it should fail.
  SET(SAVE_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
  SET(SAVE_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
  IF(CMAKE_C_FLAGS)
    STRING(REGEX REPLACE "-specs=/usr/lib/rpm/redhat/.*"  ""
      CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
  ENDIF()
  IF(CMAKE_CXX_FLAGS)
    STRING(REGEX REPLACE "-specs=/usr/lib/rpm/redhat/.*"  ""
      CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
  ENDIF()

  SET(CMAKE_REQUIRED_LIBRARIES "-fuse-ld=lld")
  CHECK_C_SOURCE_COMPILES("int main() {}" C_LD_LLD_RESULT)
  CHECK_CXX_SOURCE_COMPILES("int main() {}" CXX_LD_LLD_RESULT)
  IF(C_LD_LLD_RESULT AND CXX_LD_LLD_RESULT)
    FOREACH(flag
        CMAKE_C_LINK_FLAGS
        CMAKE_CXX_LINK_FLAGS
        CMAKE_EXE_LINKER_FLAGS
        CMAKE_MODULE_LINKER_FLAGS
        CMAKE_SHARED_LINKER_FLAGS
        )
      STRING_APPEND(${flag} " -fuse-ld=lld")
    ENDFOREACH()
  ENDIF()
  SET(CMAKE_C_FLAGS ${SAVE_CMAKE_C_FLAGS})
  SET(CMAKE_CXX_FLAGS ${SAVE_CMAKE_CXX_FLAGS})
  CMAKE_POP_CHECK_STATE()
ENDIF()

# Use gold on x86 if available and not explicitly disabled.
# LTO build fails with gold, so turn it off by default.
IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64"
    AND NOT WIN32
    AND NOT WITH_LTO
    AND NOT CMAKE_COMPILER_FLAG_WITH_LTO)
  OPTION(USE_LD_GOLD "Use GNU gold linker" ON)
ELSE()
  OPTION(USE_LD_GOLD "Use GNU gold linker" OFF)
ENDIF()

IF (USE_LD_GOLD AND NOT (C_LD_LLD_RESULT AND CXX_LD_LLD_RESULT))
  CMAKE_PUSH_CHECK_STATE(RESET)
  SET(CMAKE_REQUIRED_LIBRARIES "-fuse-ld=gold")
  CHECK_C_SOURCE_COMPILES("int main() {}" C_LD_GOLD_RESULT)
  CHECK_CXX_SOURCE_COMPILES("int main() {}" CXX_LD_GOLD_RESULT)
  IF (C_LD_GOLD_RESULT AND CXX_LD_GOLD_RESULT AND NOT SOLARIS)
    STRING_APPEND(CMAKE_C_LINK_FLAGS " -fuse-ld=gold")
    STRING_APPEND(CMAKE_CXX_LINK_FLAGS " -fuse-ld=gold")

    # Seemingly --gc-sections causes problems with randomization, so only
    # turn it on if we are not making a randomized build.
    # The --gc-sections seems to be causing OOM errors when building 32-bit
    # binaries for Ubuntu 18.04, do not use it there.
    IF (NOT LINK_RANDOMIZE AND NOT LINUX_UBUNTU_18_04)
      STRING_APPEND(CMAKE_C_LINK_FLAGS " -Wl,--gc-sections")
      STRING_APPEND(CMAKE_CXX_LINK_FLAGS " -Wl,--gc-sections")
    ENDIF()
  ENDIF()
  CMAKE_POP_CHECK_STATE()
ENDIF()

# Linker/binutils bug in Fedora 28, undefined reference to symbol '__bss_start'
# Similar bug on 32bit Ubuntu.
# Fedora 34: undefined reference to symbol 'crc32_z@@ZLIB_1.2.9'
IF(LINUX_FEDORA_28 OR LINUX_FEDORA_34 OR LINUX_UBUNTU_16_04)
  IF(NOT CMAKE_CXX_LINK_FLAGS MATCHES "-fuse-ld=lld" AND
      NOT CMAKE_CXX_LINK_FLAGS MATCHES "-fuse-ld=gold")
    STRING_APPEND(CMAKE_CXX_LINK_FLAGS " -Wl,--copy-dt-needed-entries")
  ENDIF()
ENDIF()

IF(LINUX)
  FIND_PROGRAM(PATCHELF_EXECUTABLE patchelf)

  IF(PATCHELF_EXECUTABLE)
    EXECUTE_PROCESS(
      COMMAND ${PATCHELF_EXECUTABLE} --version
      OUTPUT_VARIABLE PATCHELF_VERSION
      OUTPUT_STRIP_TRAILING_WHITESPACE
      )
    STRING(REPLACE "patchelf" "" PATCHELF_VERSION "${PATCHELF_VERSION}")
    MESSAGE(STATUS "Using patchelf: ${PATCHELF_EXECUTABLE}, version: ${PATCHELF_VERSION}")
    IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND PATCHELF_VERSION VERSION_LESS "0.14.5")
      SET(PATCHELF_PAGE_SIZE_ARGS --page-size 65536)
    ENDIF()
  ENDIF()
ENDIF()

include(fuzzer)

INCLUDE(CheckIncludeFiles)
INCLUDE(CheckFunctionExists)

include(curl)
MYSQL_CHECK_CURL()

include(zlib)
MYSQL_CHECK_ZLIB()

IF (PYTHON_DEPS AND NOT HAVE_PYTHON)
  MESSAGE(FATAL_ERROR "PYTHON_DEPS should not be used when python is not available.")
ENDIF()

IF(BUNDLED_OPENSSL_DIR)
  IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
    # this will force the MYSQL_CHECK_SSL() macro to use shared libraries
    SET(LINUX_STANDALONE 1)
  ENDIF()
  SET(WITH_SSL "${BUNDLED_OPENSSL_DIR}")
ELSE()
  SET(WITH_SSL "system")
ENDIF()

INCLUDE(ssl)
MYSQL_CHECK_SSL()

SET(OPENSSL_VERSION_ID "${OPENSSL_VERSION_MAJOR}${OPENSSL_VERSION_MINOR}${OPENSSL_FIX_VERSION}")
ADD_DEFINITIONS(-DOPENSSL_VERSION_ID=${OPENSSL_VERSION_ID})

INCLUDE(bison)

if(NOT BUILD_SOURCE_PACKAGE)
  if(BUNDLED_ANTLR_DIR)
    set(ANTLR4_INCLUDE_DIRS "${BUNDLED_ANTLR_DIR}/include")
    set(ANTLR4_LIBRARIES "${BUNDLED_ANTLR_DIR}/lib")
  else()
    find_package(antlr4-runtime REQUIRED)
    get_filename_component(ANTLR4_INCLUDE_DIRS ${ANTLR4_INCLUDE_DIR} DIRECTORY)
    set(ANTLR4_LIBRARIES "${ANTLR4_LIB_DIR}")
    unset(ANTLR4_FOUND)
    unset(ANTLR4_INCLUDE_DIR)
    unset(ANTLR4_LIB_DIR)
  endif()

  INCLUDE(FindANTLR4)

  if(NOT ANTLR4_INCLUDE_DIRS OR NOT ANTLR4_LIBRARIES)
    message(FATAL_ERROR "Could not find antlr4 runtime!")
  endif()

  if(NOT ANTLR4_VERSION_SHORT VERSION_GREATER_EQUAL "4.10")
    # we're storing files generated by antlr4 4.10.1, so we require at least
    # that version
    message(FATAL_ERROR "Required version of antlr4 runtime is at least 4.10")
  endif()
endif()

if(HAVE_PYTHON)
  SET(MINIMUM_PYTHON_VERSION "3.8")
  IF(NOT USE_PYTHON_VERSION)
    SET(USE_PYTHON_VERSION "${MINIMUM_PYTHON_VERSION}")
  ENDIF()

  IF(USE_PYTHON_VERSION VERSION_LESS MINIMUM_PYTHON_VERSION)
    MESSAGE(FATAL_ERROR "Minimum supported Python version is ${MINIMUM_PYTHON_VERSION}")
  ENDIF()

  FUNCTION(GET_PYTHON_VERSION python_dir output_var)
    FILE(GLOB_RECURSE patch_level "${python_dir}/patchlevel.h")

    IF(patch_level AND EXISTS "${patch_level}")
      FILE(STRINGS "${patch_level}" python_version_str
           REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"")
      STRING(REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"]+)\".*" "\\1"
                           python_version_str "${python_version_str}")
      SET(${output_var} "${python_version_str}" PARENT_SCOPE)
    ELSE()
      MESSAGE(FATAL_ERROR "Couldn't find patchlevel.h in ${python_dir}")
    ENDIF()
  ENDFUNCTION()

  # On windows stores the path to the python libraries since they will be bundled
  IF(WIN32)
    FIND_PACKAGE(PythonLibs "${USE_PYTHON_VERSION}" REQUIRED)
    # PYTHON_PATH and PYTHON_FOLDER are used to properly copy the python libraries where needed
    # By default they take the value of PB2WORKDIR and PYTHON_SOURCENAME env vars
    IF(NOT PYTHON_PATH AND NOT PYTHON_FOLDER)
      SET(PYTHON_PATH "$ENV{PB2WORKDIR}")
      SET(PYTHON_FOLDER "$ENV{PYTHON_SOURCENAME}")
    ENDIF()

    IF(NOT PYTHON_PATH AND NOT PYTHON_FOLDER)
      GET_FILENAME_COMPONENT(PYTHON_PATH ${PYTHON_INCLUDE_DIRS} DIRECTORY)
      GET_FILENAME_COMPONENT(PYTHON_FOLDER ${PYTHON_PATH} NAME)
      GET_FILENAME_COMPONENT(PYTHON_PATH ${PYTHON_PATH} DIRECTORY)
    ENDIF()

    IF(PYTHON_PATH AND PYTHON_FOLDER)
      SET(PYTHON_ROOT "${PYTHON_PATH}/${PYTHON_FOLDER}")
      FILE(TO_CMAKE_PATH "${PYTHON_ROOT}" PYTHON_ROOT)
      SET(PYTHON_LIBRARY_PATH "${PYTHON_ROOT}/Lib")
      SET(PYTHON_DLLS_PATH "${PYTHON_ROOT}/DLLs")
      SET(PYTHON_EXECUTABLE "${PYTHON_ROOT}/python.exe")
    ENDIF()
  ELSEIF(BUNDLED_PYTHON_DIR)
    # If we're using a Python build for bundling, ensure we build against it too
    GET_PYTHON_VERSION("${BUNDLED_PYTHON_DIR}" PYTHONLIBS_VERSION_STRING)
    GET_MAJOR_MINOR("${PYTHONLIBS_VERSION_STRING}" PYTHONLIBS_MAJOR_MINOR)
    FIND_PROGRAM(PYTHON_EXECUTABLE
      NAMES
        python${PYTHONLIBS_MAJOR_MINOR}mu
        python${PYTHONLIBS_MAJOR_MINOR}m
        python${PYTHONLIBS_MAJOR_MINOR}u
        python${PYTHONLIBS_MAJOR_MINOR}
      HINTS
        "${BUNDLED_PYTHON_DIR}/bin"
      NO_DEFAULT_PATH
    )
    FIND_PATH(PYTHON_INCLUDE_DIRS
      NAMES
        Python.h
      HINTS
        "${BUNDLED_PYTHON_DIR}/include"
      PATH_SUFFIXES
        python${PYTHONLIBS_MAJOR_MINOR}mu
        python${PYTHONLIBS_MAJOR_MINOR}m
        python${PYTHONLIBS_MAJOR_MINOR}u
        python${PYTHONLIBS_MAJOR_MINOR}
      NO_DEFAULT_PATH
    )
    FIND_LIBRARY(PYTHON_LIBRARIES
      NAMES
        python${PYTHONLIBS_MAJOR_MINOR}mu
        python${PYTHONLIBS_MAJOR_MINOR}m
        python${PYTHONLIBS_MAJOR_MINOR}u
        python${PYTHONLIBS_MAJOR_MINOR}
      HINTS
        "${BUNDLED_PYTHON_DIR}/lib"
      NO_DEFAULT_PATH
    )
    IF(NOT PYTHON_LIBRARIES MATCHES ".*\\.a$")
      SET(BUNDLED_SHARED_PYTHON "YES")
    ENDIF()
  ELSE()
    FIND_PACKAGE(PythonLibs "${USE_PYTHON_VERSION}" REQUIRED)
    IF(NOT PYTHON_EXECUTABLE)
      # Can't use FIND_PACKAGE(Python ...) as it finds the latest Python interp and not the version we want
      GET_MAJOR_MINOR("${PYTHONLIBS_VERSION_STRING}" PYTHONLIBS_MAJOR_MINOR)
      FIND_PROGRAM(PYTHON_EXECUTABLE
        NAMES
          python${PYTHONLIBS_MAJOR_MINOR}mu
          python${PYTHONLIBS_MAJOR_MINOR}m
          python${PYTHONLIBS_MAJOR_MINOR}u
          python${PYTHONLIBS_MAJOR_MINOR}
      )
    ENDIF()
  ENDIF()
endif()

IF (JIT_EXECUTOR_LIB)
  FIND_LIBRARY(JIT_EXECUTOR_LIBRARY
    NAMES
      jitexecutor
    HINTS
      "${JIT_EXECUTOR_LIB}"
    NO_DEFAULT_PATH)

  INCLUDE_DIRECTORIES(SYSTEM "${JIT_EXECUTOR_LIB}")
  MESSAGE(STATUS "Jit Executor Library: ${JIT_EXECUTOR_LIBRARY}")  
  SET(HAVE_JS 1)
  SET(HAVE_JIT_EXECUTOR 1)
  ADD_DEFINITIONS(-DHAVE_JS)
  ADD_DEFINITIONS(-DHAVE_JIT_EXECUTOR)
ENDIF()



IF(WIN32)
  # Speed up build process excluding unused header files
  # Also definitely resolves the trouble of using both
  # windows.h and winsock2.h
  ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN)
  ADD_DEFINITIONS(-DNOMINMAX)
  ADD_DEFINITIONS(-DNOGDI)
  ADD_DEFINITIONS(-DNOMB)
  ADD_DEFINITIONS(-DNOMEMMGR)
  ADD_DEFINITIONS(-DNOMETAFILE)
  ADD_DEFINITIONS(-DNOSCROLL)
  ADD_DEFINITIONS(-DNOSERVICE)
  ADD_DEFINITIONS(-DNOSOUND)
  ADD_DEFINITIONS(-DNOTEXTMETRIC)
  ADD_DEFINITIONS(-DNOWH)
  ADD_DEFINITIONS(-DNOWINOFFSETS)
  ADD_DEFINITIONS(-DNOCOMM)
  ADD_DEFINITIONS(-DNOKANJI)
  ADD_DEFINITIONS(-DNOHELP)
  ADD_DEFINITIONS(-DNOPROFILER)
  ADD_DEFINITIONS(-DNODEFERWINDOWPOS)
  ADD_DEFINITIONS(-DNOMCX)

  # Silence warnings about functions that are perfectly fine for posix
  ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)

  # Sets the windows versions that should be supported
  ADD_DEFINITIONS(-DNTDDI_VERSION=0x06000100 -D_WIN32_WINNT=0x0600)

  IF (MSVC)
      # Enable parallel builds
      ADD_COMPILE_OPTIONS(/MP)
      # Make sure source files and execution context are set to UTF8
      ADD_COMPILE_OPTIONS(/utf-8)
      # Make sure we keep with the language standards conformance
      ADD_COMPILE_OPTIONS(/permissive-)
  ENDIF()

ELSE()
  # Non windows builds should build dev packages by default
  SET(WITH_DEV "1")
ENDIF()

IF(PYTHONLIBS_FOUND OR BUNDLED_PYTHON_DIR)
  set(HAVE_PYTHON "YES")         # Variable for CMake processing
  IF(BUNDLED_PYTHON_DIR)
    add_definitions(-DHAVE_PYTHON=2) # 2 means bundled
  ELSE()
    add_definitions(-DHAVE_PYTHON=1) # 1 means normal (or windows)
  ENDIF()
  message(STATUS "Python ${PYTHONLIBS_VERSION_STRING}")
  message(STATUS "PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}")
  message(STATUS "PYTHON_INCLUDE_DIRS: ${PYTHON_INCLUDE_DIRS}")
  message(STATUS "PYTHON_LIBRARIES: ${PYTHON_LIBRARIES}")

  IF(NOT PYTHONLIBS_VERSION_STRING)
    MESSAGE(FATAL_ERROR "Couldn't detect Python's version")
  ENDIF()

  # supported Python versions: (v >= 3.8)
  IF(PYTHONLIBS_VERSION_STRING VERSION_LESS "3.8")
    MESSAGE(FATAL_ERROR "Python support requires Python 3.8 or greater")
  ENDIF()

  GET_MAJOR_MINOR("${PYTHONLIBS_VERSION_STRING}" PYTHONLIBS_MAJOR_MINOR)
  message(STATUS "Python major.minor: ${PYTHONLIBS_MAJOR_MINOR}")

  # TODO: Temporary flag indicating python is static as well
  # Proper logic should be added to correctly configure this
  IF(WITH_STATIC_MSVCRT)
    SET(WITH_STATIC_PYTHON_LIB 1)
  ENDIF()

ELSE()
  message(WARNING "Python is unavailable: building without Python support.")
ENDIF()

# Sets default linking to static if not already defined
if(NOT DEFINED MYSQLCLIENT_STATIC_LINKING)
  set(MYSQLCLIENT_STATIC_LINKING TRUE)
endif()

### Bundling of OpenSSL libraries (if needed)
# macro MYSQL_CHECK_SSL_DLLS() adapted for Shell
IF (WITH_SSL_PATH AND (APPLE OR WIN32 OR LINUX_STANDALONE))
  SET(BUNDLED_OPENSSL 1)
  MESSAGE(STATUS "WITH_SSL_PATH ${WITH_SSL_PATH}")

  # In MySQL Server 8.0.4 and up, OpenSSL is linked dynamically and
  # preferably linked against the system installed OpenSSL. But there
  # is no standard system installed OpenSSL on macOS or Windows so we
  # bundle the library copying it from the server install we link
  # against.

  IF(WIN32)
    GET_FILENAME_COMPONENT(CRYPTO_NAME "${CRYPTO_LIBRARY}" NAME_WE)
    GET_FILENAME_COMPONENT(OPENSSL_NAME "${OPENSSL_LIBRARY}" NAME_WE)

    # Different naming scheme for the matching .dll as of SSL 1.1
    # OpenSSL 3.x Look for libcrypto-3-x64.dll or libcrypto-3.dll
    # OpenSSL 1.1 Look for libcrypto-1_1-x64.dll or libcrypto-1_1.dll
    # OpenSSL 1.0 Look for libeay32.dll
    SET(SSL_MSVC_VERSION_SUFFIX)
    SET(SSL_MSVC_ARCH_SUFFIX)
    IF(OPENSSL_VERSION_MINOR VERSION_EQUAL 1)
      SET(SSL_MSVC_VERSION_SUFFIX "-1_1")
      SET(SSL_MSVC_ARCH_SUFFIX "-x64")
    ENDIF()
    IF(OPENSSL_VERSION_MAJOR VERSION_EQUAL 3)
      SET(SSL_MSVC_VERSION_SUFFIX "-3")
      SET(SSL_MSVC_ARCH_SUFFIX "-x64")
    ENDIF()

    MESSAGE(STATUS "===> WITH_SSL_PATH: ${WITH_SSL_PATH}")
    MESSAGE(STATUS "===> CRYPTO_NAME: ${CRYPTO_NAME}")
    MESSAGE(STATUS "===> OPENSSL_NAME: ${OPENSSL_NAME}")
    MESSAGE(STATUS "===> SSL_MSVC_VERSION_SUFFIX: ${SSL_MSVC_VERSION_SUFFIX}")
    MESSAGE(STATUS "===> SSL_MSVC_ARCH_SUFFIX: ${SSL_MSVC_ARCH_SUFFIX}")

    FIND_FILE(HAVE_CRYPTO_DLL
      NAMES
      "${CRYPTO_NAME}${SSL_MSVC_VERSION_SUFFIX}${SSL_MSVC_ARCH_SUFFIX}.dll"
      "${CRYPTO_NAME}${SSL_MSVC_VERSION_SUFFIX}.dll"
      PATHS "${WITH_SSL_PATH}/bin"
      NO_DEFAULT_PATH
      )
    FIND_FILE(HAVE_OPENSSL_DLL
      NAMES
      "${OPENSSL_NAME}${SSL_MSVC_VERSION_SUFFIX}${SSL_MSVC_ARCH_SUFFIX}.dll"
      "${OPENSSL_NAME}${SSL_MSVC_VERSION_SUFFIX}.dll"
      PATHS "${WITH_SSL_PATH}/bin"
      NO_DEFAULT_PATH
      )

    MESSAGE(STATUS "HAVE_CRYPTO_DLL ${HAVE_CRYPTO_DLL}")
    MESSAGE(STATUS "HAVE_OPENSSL_DLL ${HAVE_OPENSSL_DLL}")
  ELSE()
    EXECUTE_PROCESS(
      COMMAND readlink "${CRYPTO_LIBRARY}" OUTPUT_VARIABLE CRYPTO_VERSION
      OUTPUT_STRIP_TRAILING_WHITESPACE)
    EXECUTE_PROCESS(
      COMMAND readlink "${OPENSSL_LIBRARY}" OUTPUT_VARIABLE OPENSSL_VERSION
      OUTPUT_STRIP_TRAILING_WHITESPACE)

    MESSAGE(STATUS "CRYPTO_VERSION ${CRYPTO_VERSION}")
    MESSAGE(STATUS "OPENSSL_VERSION ${OPENSSL_VERSION}")

    GET_FILENAME_COMPONENT(CRYPTO_DIRECTORY "${CRYPTO_LIBRARY}" DIRECTORY)
    GET_FILENAME_COMPONENT(OPENSSL_DIRECTORY "${OPENSSL_LIBRARY}" DIRECTORY)

    MESSAGE(STATUS "CRYPTO_DIRECTORY ${CRYPTO_DIRECTORY}")
    MESSAGE(STATUS "OPENSSL_DIRECTORY ${OPENSSL_DIRECTORY}")

    SET(CRYPTO_FULL_NAME "${CRYPTO_DIRECTORY}/${CRYPTO_VERSION}")
    SET(OPENSSL_FULL_NAME "${OPENSSL_DIRECTORY}/${OPENSSL_VERSION}")

    MESSAGE(STATUS "CRYPTO_FULL_NAME ${CRYPTO_FULL_NAME}")
    MESSAGE(STATUS "OPENSSL_FULL_NAME ${OPENSSL_FULL_NAME}")
  ENDIF()

  FIND_PATH(OPENSSL_MODULES_DIR
    NAMES ossl-modules
    NO_CMAKE_PATH
    NO_CMAKE_ENVIRONMENT_PATH
    HINTS ${WITH_SSL_PATH}
    PATH_SUFFIXES lib lib64
  )

  IF(OPENSSL_MODULES_DIR)
    SET(OPENSSL_MODULES_DIR "${OPENSSL_MODULES_DIR}/ossl-modules")
    FILE(TO_CMAKE_PATH "${OPENSSL_MODULES_DIR}" OPENSSL_MODULES_DIR)
    MESSAGE(STATUS "OPENSSL_MODULES_DIR ${OPENSSL_MODULES_DIR}")

    IF(WIN32)
      SET(_module_ext "dll")
    ELSEIF(APPLE)
      SET(_module_ext "dylib")
    ELSE()
      SET(_module_ext "so")
    ENDIF()

    FILE(GLOB OPENSSL_MODULES_LIST "${OPENSSL_MODULES_DIR}/*.${_module_ext}")
    MESSAGE(STATUS "OPENSSL_MODULES_LIST ${OPENSSL_MODULES_LIST}")

    add_definitions(-DBUNDLE_OPENSSL_MODULES)
  ENDIF()
ENDIF()

if (NOT BUILD_SOURCE_PACKAGE)
  IF(NOT MYSQL_SOURCE_DIR)
    SET(MYSQL_SOURCE_DIR "../mysql" CACHE PATH "Path to MySQL source directory")
    MESSAGE(STATUS "MYSQL_SOURCE_DIR has been set to: ${MYSQL_SOURCE_DIR}")
  ENDIF()

  IF(NOT MYSQL_BUILD_DIR)
    SET(MYSQL_BUILD_DIR "${MYSQL_SOURCE_DIR}/bld" CACHE PATH "Path to MySQL build directory")
    MESSAGE(STATUS "MYSQL_BUILD_DIR has been set to: ${MYSQL_BUILD_DIR}")
  ENDIF()

  # Needed for protobuf header paths, which are needed to use libmysqlxclient
  INCLUDE(protobuf)
  INCLUDE(FindMySQLx)
  # Needed for X protocol error codes
  CONFIGURE_FILE(${MYSQL_SOURCE_DIR}/plugin/x/src/xpl_error.h ${CMAKE_SOURCE_DIR}/mysqlshdk/libs/db/mysqlx/xpl_error.h COPYONLY)
  INCLUDE(lz4)
  # Add lz4 library (Dependency for the X Client Lib)
  MYSQL_CHECK_LZ4()
endif()


##
## Installation location
##
SET(INSTALL_BINDIR "bin")

IF(LIBEXECDIR)
  ADD_DEFINITIONS(-DLIBEXECDIR="${LIBEXECDIR}")
  SET(INSTALL_LIBEXECDIR "${LIBEXECDIR}/mysqlsh")
ELSE()
  ADD_DEFINITIONS(-DLIBEXECDIR="libexec")
  SET(INSTALL_LIBEXECDIR "libexec/mysqlsh")
ENDIF()

SET(INSTALL_LIBDIR "lib/mysqlsh")

IF(MYSQL_BUILD_DIR)
  SET(INSTALL_MYSQL_PLUGINS_DIR "lib/mysql/plugins")
  SET(_plugin_subdirs "${MYSQL_BUILD_DIR}/plugin_output_directory")
  SET(_plugin_ext "so")

  IF(WIN32)
    STRING(APPEND _plugin_subdirs "/${CMAKE_BUILD_TYPE}")
    SET(_plugin_ext "dll")
  ENDIF()

  FILE(GLOB AUTH_CLIENT_PLUGINS "${_plugin_subdirs}/authentication_*_client.${_plugin_ext}")

  IF(EXISTS "${_plugin_subdirs}/mysql_native_password.${_plugin_ext}")
    LIST(APPEND AUTH_CLIENT_PLUGINS "${_plugin_subdirs}/mysql_native_password.${_plugin_ext}")
  ENDIF()

  IF(AUTH_CLIENT_PLUGINS)
    SET(AUTH_CLIENT_PLUGINS_COPY ${AUTH_CLIENT_PLUGINS})
    SET(EXPECTED_AUTH_CLIENT_PLUGINS
      "authentication_kerberos_client"
      "authentication_ldap_sasl_client"
      "authentication_oci_client"
      "authentication_openid_connect_client"
      "mysql_native_password"
    )

    IF(${OPENSSL_MAJOR_MINOR_FIX_VERSION} VERSION_GREATER_EQUAL "1.1.1")
      IF(${MYSQL_VERSION} VERSION_GREATER_EQUAL "8.2.0")
        LIST(APPEND EXPECTED_AUTH_CLIENT_PLUGINS "authentication_webauthn_client")
      ENDIF()
    ENDIF()

    FOREACH(_plugin ${AUTH_CLIENT_PLUGINS})
      FOREACH(_expected_plugin ${EXPECTED_AUTH_CLIENT_PLUGINS})
        IF(_plugin MATCHES ".+${_expected_plugin}\..+")
          LIST(REMOVE_ITEM EXPECTED_AUTH_CLIENT_PLUGINS "${_expected_plugin}")
          LIST(REMOVE_ITEM AUTH_CLIENT_PLUGINS_COPY "${_plugin}")

          IF(_expected_plugin STREQUAL "authentication_webauthn_client")
            SET(_library_subdirs "${MYSQL_BUILD_DIR}/library_output_directory")

            IF(WIN32)
              STRING(APPEND _library_subdirs "/${CMAKE_BUILD_TYPE}")
              FILE(GLOB AUTH_CLIENT_PLUGINS_DEPS "${_library_subdirs}/fido2.dll")
            ELSEIF(APPLE)
              FILE(GLOB AUTH_CLIENT_PLUGINS_DEPS "${_library_subdirs}/libfido2*.dylib")
            ELSE()
              FILE(GLOB AUTH_CLIENT_PLUGINS_DEPS "${_library_subdirs}/libfido2.so*")
            ENDIF()

            IF(NOT AUTH_CLIENT_PLUGINS_DEPS)
              MESSAGE(FATAL_ERROR "Couldn't find libfido2, a dependency of authentication_webauthn_client")
            ENDIF()
          ENDIF()
          BREAK()
        ENDIF()
      ENDFOREACH()
    ENDFOREACH()

    IF(AUTH_CLIENT_PLUGINS_COPY)
      MESSAGE(FATAL_ERROR "Found unexpected client authentication plugins: ${AUTH_CLIENT_PLUGINS_COPY}")
    ENDIF()

    IF(EXPECTED_AUTH_CLIENT_PLUGINS)
      MESSAGE(FATAL_ERROR "Didn't find expected client authentication plugins: ${EXPECTED_AUTH_CLIENT_PLUGINS}")
    ENDIF()

    ADD_DEFINITIONS(-DDEFAULT_MYSQL_PLUGIN_DIR="${INSTALL_MYSQL_PLUGINS_DIR}")
  ENDIF()
ENDIF()

SET(INSTALL_INCLUDEDIR "include/mysqlsh")
SET(INSTALL_SHAREDIR "share/mysqlsh")
SET(INSTALL_DOCDIR "share/mysqlsh/Docs")
SET(INSTALL_MANDIR "share/man")

##
## Binary location
##
IF(MSVC)
  SET(CONFIG_BINARY_DIR "${CMAKE_BINARY_DIR}/$<CONFIG>")
ELSE()
  SET(CONFIG_BINARY_DIR "${CMAKE_BINARY_DIR}")
ENDIF()

##
## Configure style system detection
##
INCLUDE(configure.cmake)

## The following definition is required to have memset_s
## available on the platforms that implement it
CHECK_FUNCTION_EXISTS (memset_s HAVE_MEMSET_S)
IF (HAVE_MEMSET_S)
  ADD_DEFINITIONS(-D__STDC_WANT_LIB_EXT1__=1)
ENDIF()
CONFIGURE_FILE(mysh_config.h.cmake   ${CMAKE_BINARY_DIR}/include/mysh_config.h)

MACRO (MY_CHECK_CXX_COMPILER_FLAG FLAG RESULT)
  CMAKE_PUSH_CHECK_STATE()
  STRING_APPEND(CMAKE_REQUIRED_FLAGS " ${FLAG}")
  CHECK_CXX_SOURCE_COMPILES("int main(void) { return 0; }" ${RESULT}
    FAIL_REGEX "unknown argument ignored"
    FAIL_REGEX "argument unused during compilation"
    FAIL_REGEX "unsupported .*option"
    FAIL_REGEX "unknown .*option"
    FAIL_REGEX "unrecognized .*option"
    FAIL_REGEX "ignoring unknown option"
    FAIL_REGEX "[Ww]arning: [Oo]ption"
    FAIL_REGEX "error: visibility"
    FAIL_REGEX "warning: visibility"
    )
  CMAKE_POP_CHECK_STATE()
ENDMACRO()

# By default the shell uses Multi-threaded Dynamic RunTime
# Only sets Static if needed
IF(WIN32)
  IF(WITH_STATIC_MSVCRT)
    FOREACH(flag_var
          CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
          CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
       IF(${flag_var} MATCHES "/MD")
          STRING(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
       ENDIF(${flag_var} MATCHES "/MD")
    ENDFOREACH(flag_var)
  ENDIF()
ENDIF()

# Set NDEBUG for non-debug project types.
FOREACH(BUILD_TYPE RELEASE RELWITHDEBINFO MINSIZEREL)
  FOREACH(LANG C CXX)
    SET(CMAKE_${LANG}_FLAGS_${BUILD_TYPE} "${CMAKE_${LANG}_FLAGS_${BUILD_TYPE}} -DNDEBUG ")
  ENDFOREACH()
ENDFOREACH()

IF(NOT CMAKE_BUILD_TYPE
    AND NOT CMAKE_GENERATOR MATCHES "Visual Studio"
    AND NOT CMAKE_GENERATOR MATCHES "Xcode")
    # This is the case of no CMAKE_BUILD_TYPE choosen, typical for VS and Xcode
    # or if custom C flags are set. In VS and Xcode for non-Debug configurations
    # NDEBUG is already correctly set.
    ADD_DEFINITIONS(-DNDEBUG)
ENDIF()

# Includes common to the whole project
INCLUDE_DIRECTORIES(
            ${CMAKE_BINARY_DIR}
            ${PROJECT_SOURCE_DIR}/ext
            ${MYSQL_INCLUDE_DIRS}
            ${MYSQLX_INCLUDE_DIRS}
            ${PROTOBUF_INCLUDE_DIRS}
            ${MYSQL_SOURCE_DIR}/extra/rapidjson/include
            SYSTEM ${ANTLR4_INCLUDE_DIRS})

###
### Handle User Options
###

if(BUNDLED_SSH_DIR)
  set(libssh_DIR ${BUNDLED_SSH_DIR})
endif()

find_package(libssh 0.9.2 REQUIRED)

message(STATUS "Found libssh: ${libssh_VERSION}, using config: ${libssh_CONFIG}")

add_definitions(-DMYSH_VERSION="${MYSH_VERSION}" -DMYSH_BUILD_ID="${MYSH_BUILD_ID}" -DEXTRA_NAME_SUFFIX="${EXTRA_NAME_SUFFIX}")
if(WIN32)
  add_definitions(-DMYSH_VERSION_WIN="${MYSH_VERSION_WIN}")
endif()

##
## Prepares the python dependencies
###

IF(PYTHON_DEPS)

  IF (NOT WIN32 AND NOT BUNDLED_PYTHON_DIR)
    # PYTHON_DEPS_PATH will cause the shell initialization to explicitly add the
    # bundled packages to the PYTHONPATH.
    # Only needed on this case, as when WIN32 or BUNDLED_PYTHON_DIR they are
    # automatically loaded by the site.py logic
    ADD_DEFINITIONS(-DPYTHON_DEPS_PATH="lib/python${PYTHONLIBS_MAJOR_MINOR}/site-packages")
  ENDIF()

  ADD_DEFINITIONS(-DPYTHON_DEPS)
ENDIF()

IF(NOT BUNDLED_MYSQLBINLOG)
  IF(WIN32)
    SET(BUNDLED_MYSQLBINLOG "${MYSQL_BUILD_DIR}/runtime_output_directory/${CMAKE_BUILD_TYPE}/mysqlbinlog.exe")
  ELSE()
    SET(BUNDLED_MYSQLBINLOG "${MYSQL_BUILD_DIR}/runtime_output_directory/mysqlbinlog")
  ENDIF()
ENDIF()

IF(NOT BUILD_SOURCE_PACKAGE AND NOT EXISTS "${BUNDLED_MYSQLBINLOG}")
  message(FATAL_ERROR "The bundled 'mysqlbinlog' binary was not found, please compile it using: cmake --build '${MYSQL_BUILD_DIR}' --target mysqlbinlog")
ENDIF()

IF(BUNDLED_MYSQL_CONFIG_EDITOR OR BUNDLED_MYSQLBINLOG)
  add_definitions(-DHAVE_LIBEXEC_DIR)
ENDIF()

IF(WITH_TESTS)
  ###
  ### Unit-test support
  ###
  add_subdirectory(unittest)
  add_subdirectory(tests/fuzz)
ENDIF()

###
### Build Projects
###

ADD_SUBDIRECTORY(plugins/external)
ADD_SUBDIRECTORY(python)
ADD_SUBDIRECTORY(mysqlshdk)
ADD_SUBDIRECTORY(modules)
ADD_SUBDIRECTORY(res)
ADD_SUBDIRECTORY(src)
add_subdirectory(samples)
add_subdirectory(mysql-secret-store)
add_subdirectory(tests/bench)

IF(UNIX)
  ADD_SUBDIRECTORY(man)
ENDIF()

CHECK_STAN_OPTIONS()

IF(EXISTS ${CMAKE_SOURCE_DIR}/internal/CMakeLists.txt)
  ADD_SUBDIRECTORY(internal)
ENDIF()

###
### Optionally bundle Visual C++ Redistributable for Visual Studio
###

if(WIN32)
  option(BUNDLE_RUNTIME_LIBRARIES "Install Windows runtime libraries" OFF)
  if(BUNDLE_RUNTIME_LIBRARIES)
     INSTALL_VS_REDIST(main)
  endif()
endif()

#
# Create the INFO_SRC and INFO_BIN files
# =========================
#
# Will set GIT_EXECUTABLE and GIT_FOUND
FIND_PACKAGE(Git)

CONFIGURE_FILE(
    ${CMAKE_SOURCE_DIR}/cmake/info_macros.cmake.in
    ${CMAKE_BINARY_DIR}/info_macros.cmake @ONLY)

INCLUDE(info_src)
INSTALL(FILES ${CMAKE_BINARY_DIR}/Docs/INFO_SRC COMPONENT main DESTINATION ${INSTALL_DOCDIR})

IF (NOT BUILD_SOURCE_PACKAGE)
  INCLUDE(info_bin)
  INSTALL(FILES ${CMAKE_BINARY_DIR}/Docs/INFO_BIN COMPONENT main DESTINATION ${INSTALL_DOCDIR})
ENDIF()


###
### Packaging and other dependecy copying
###

INCLUDE(packaging)

if(CPACK_WIX_MYSQLSH_USE_CUSTOM_ACTION)
  add_subdirectory(packaging/wix4/custom_action)
endif()
